#!/bin/bash
###############################################################################
#       脚本名称: test_yaml.sh
#       功能描述: YAML配置管理适配层(yaml.sh)单元测试
#       作者: 林再来
#       日期: 2025-10-15
#       版本: 1.0
#       功能:
#           1、测试YAML配置管理的业务逻辑
#           2、验证多环境配置、变更检测、快照等功能
#           3、确保配置管理特性的正确性
###############################################################################

# 切换到脚本目录
cd "$(dirname "${BASH_SOURCE[0]}")" || exit 1

# 加载测试框架
source ../test_framework.sh

# 加载被测模块（会自动加载依赖的utils_yq.sh）
source ../../../scripts/config/yaml.sh

# 创建临时测试目录
TEST_DIR=$(create_test_dir)

###############################################################################
# 测试用例
###############################################################################

# 测试: 读取配置值
test_yaml_read() {
    local test_file="$TEST_DIR/config_read.yaml"
    cat > "$test_file" << 'EOF'
app:
  name: MyApp
  port: 8080
database:
  host: localhost
EOF
    
    local name=$(yaml_read "$test_file" ".app.name" 2>/dev/null)
    assert_equals "MyApp" "$name"
    
    local host=$(yaml_read "$test_file" ".database.host" 2>/dev/null)
    assert_equals "localhost" "$host"
}

# 测试: 写入配置值
test_yaml_write() {
    local test_file="$TEST_DIR/config_write.yaml"
    echo "{}" > "$test_file"
    
    yaml_write "$test_file" ".app.name" "TestApp" "string" 2>/dev/null
    yaml_write "$test_file" ".app.port" "8080" "number" 2>/dev/null
    
    local name=$(yaml_read "$test_file" ".app.name" 2>/dev/null)
    assert_equals "TestApp" "$name"
    
    local port=$(yaml_read "$test_file" ".app.port" 2>/dev/null)
    assert_equals "8080" "$port"
}

# 测试: 删除配置键
test_yaml_delete() {
    local test_file="$TEST_DIR/config_delete.yaml"
    cat > "$test_file" << 'EOF'
app:
  name: MyApp
  temp: to_delete
  port: 8080
EOF
    
    yaml_delete "$test_file" ".app.temp" 2>/dev/null
    
    local temp=$(yaml_read "$test_file" ".app.temp" 2>/dev/null)
    assert_empty "$temp" "删除的配置应该不存在"
    
    local name=$(yaml_read "$test_file" ".app.name" 2>/dev/null)
    assert_equals "MyApp" "$name" "其他配置应该保留"
}

# 测试: 多环境配置合并
test_yaml_merge_env() {
    local base_file="$TEST_DIR/base.yaml"
    local env_file="$TEST_DIR/prod.yaml"
    local output_file="$TEST_DIR/merged.yaml"
    
    cat > "$base_file" << 'EOF'
app:
  name: MyApp
  version: 1.0.0
  debug: true
database:
  host: localhost
  port: 3306
EOF
    
    cat > "$env_file" << 'EOF'
app:
  debug: false
  env: production
database:
  host: prod-db.example.com
  pool_size: 50
EOF
    
    yaml_merge_env "$base_file" "$env_file" "$output_file" 2>/dev/null
    
    # 验证base的值
    local version=$(yaml_read "$output_file" ".app.version" 2>/dev/null)
    assert_equals "1.0.0" "$version" "base的独有配置应该保留"
    
    # 验证env覆盖的值
    local debug=$(yaml_read "$output_file" ".app.debug" 2>/dev/null)
    assert_equals "false" "$debug" "env应该覆盖base的值"
    
    local db_host=$(yaml_read "$output_file" ".database.host" 2>/dev/null)
    assert_equals "prod-db.example.com" "$db_host" "env应该覆盖base的数据库配置"
    
    # 验证env的新增配置
    local env_name=$(yaml_read "$output_file" ".app.env" 2>/dev/null)
    assert_equals "production" "$env_name" "env的新配置应该被添加"
    
    local pool_size=$(yaml_read "$output_file" ".database.pool_size" 2>/dev/null)
    assert_equals "50" "$pool_size" "env的新数据库配置应该被添加"
}

# 测试: 计算配置哈希值 - 整个文件
test_yaml_get_hash_full() {
    local test_file="$TEST_DIR/hash_test.yaml"
    cat > "$test_file" << 'EOF'
app:
  name: MyApp
  port: 8080
EOF
    
    local hash1=$(yaml_get_hash "$test_file" 2>/dev/null)
    assert_not_empty "$hash1" "哈希值不应该为空"
    
    # 修改文件
    yaml_write "$test_file" ".app.version" "1.0.0" "string" 2>/dev/null
    
    local hash2=$(yaml_get_hash "$test_file" 2>/dev/null)
    assert_not_empty "$hash2" "新哈希值不应该为空"
    assert_not_equals "$hash1" "$hash2" "修改后哈希值应该不同"
}

# 测试: 计算配置哈希值 - 指定键
test_yaml_get_hash_keys() {
    local test_file="$TEST_DIR/hash_keys_test.yaml"
    cat > "$test_file" << 'EOF'
app:
  name: MyApp
  port: 8080
  debug: true
EOF
    
    # 只计算特定键的哈希
    local hash1=$(yaml_get_hash "$test_file" ".app.name" ".app.port" 2>/dev/null)
    
    # 修改不相关的键
    yaml_write "$test_file" ".app.debug" "false" "boolean" 2>/dev/null
    
    local hash2=$(yaml_get_hash "$test_file" ".app.name" ".app.port" 2>/dev/null)
    assert_equals "$hash1" "$hash2" "未监控的键变化不应影响哈希"
    
    # 修改监控的键
    yaml_write "$test_file" ".app.port" "9090" "number" 2>/dev/null
    
    local hash3=$(yaml_get_hash "$test_file" ".app.name" ".app.port" 2>/dev/null)
    assert_not_equals "$hash2" "$hash3" "监控的键变化应该改变哈希"
}

# 测试: 保存和检查配置变更
test_yaml_save_check_changed() {
    local test_file="$TEST_DIR/changed_test.yaml"
    local hash_file="$TEST_DIR/changed_test.hash"
    
    cat > "$test_file" << 'EOF'
app:
  name: MyApp
  port: 8080
EOF
    
    # 保存初始哈希
    yaml_save_hash "$test_file" "$hash_file" 2>/dev/null
    
    # 检查变更（应该没变化，返回1）
    yaml_check_changed "$test_file" "$hash_file" 2>/dev/null
    local result=$?
    assert_equals "1" "$result" "初始状态应该没有变更"
    
    # 修改配置
    yaml_write "$test_file" ".app.port" "9090" "number" 2>/dev/null
    
    # 检查变更（应该有变化，返回0）
    assert_success yaml_check_changed "$test_file" "$hash_file"
    
    # 更新哈希
    yaml_save_hash "$test_file" "$hash_file" 2>/dev/null
    
    # 再次检查（应该没变化，返回1）
    yaml_check_changed "$test_file" "$hash_file" 2>/dev/null
    local result2=$?
    assert_equals "1" "$result2" "更新哈希后应该没有变更"
}

# 测试: 比较两个配置文件的值
test_yaml_compare_values() {
    local file1="$TEST_DIR/config1.yaml"
    local file2="$TEST_DIR/config2.yaml"
    
    cat > "$file1" << 'EOF'
app:
  name: MyApp
  port: 8080
  debug: true
EOF
    
    cat > "$file2" << 'EOF'
app:
  name: MyApp
  port: 8080
  debug: false
  version: 1.0.0
EOF
    
    # 比较相同的键
    assert_success yaml_compare_values "$file1" "$file2" ".app.name" ".app.port"
    
    # 比较不同的键
    if yaml_compare_values "$file1" "$file2" ".app.debug" 2>/dev/null; then
        echo "debug应该不同" >&2
        return 1
    fi
}

# 测试: 创建配置快照
test_yaml_create_snapshot() {
    local test_file="$TEST_DIR/snapshot_test.yaml"
    local snapshot_dir="$TEST_DIR/snapshots"
    
    cat > "$test_file" << 'EOF'
app:
  name: MyApp
  version: 1.0.0
EOF
    
    local snapshot_file=$(yaml_create_snapshot "$test_file" "$snapshot_dir" "测试快照" 2>/dev/null)
    
    assert_not_empty "$snapshot_file" "应该返回快照文件路径"
    assert_file_exists "$snapshot_file" "快照文件应该存在"
    
    # 验证快照内容
    local snap_name=$(yaml_read "$snapshot_file" ".app.name" 2>/dev/null)
    assert_equals "MyApp" "$snap_name" "快照应该包含原始数据"
    
    # 验证元数据文件
    local meta_file="${snapshot_file%.yaml}.meta"
    assert_file_exists "$meta_file" "元数据文件应该存在"
    
    assert_success grep -q "测试快照" "$meta_file"
}

# 测试: 配置回滚
test_yaml_rollback() {
    local test_file="$TEST_DIR/rollback_test.yaml"
    local snapshot_dir="$TEST_DIR/rollback_snapshots"
    
    cat > "$test_file" << 'EOF'
app:
  name: OriginalApp
  version: 1.0.0
EOF
    
    # 创建快照
    local snapshot_file=$(yaml_create_snapshot "$test_file" "$snapshot_dir" "回滚前" 2>/dev/null)
    
    # 修改配置
    yaml_write "$test_file" ".app.name" "ModifiedApp" "string" 2>/dev/null
    yaml_write "$test_file" ".app.version" "2.0.0" "string" 2>/dev/null
    
    local modified_name=$(yaml_read "$test_file" ".app.name" 2>/dev/null)
    assert_equals "ModifiedApp" "$modified_name" "配置应该被修改"
    
    # 回滚
    yaml_rollback "$test_file" "$snapshot_file" 2>/dev/null
    
    local restored_name=$(yaml_read "$test_file" ".app.name" 2>/dev/null)
    assert_equals "OriginalApp" "$restored_name" "配置应该被恢复到快照状态"
    
    local restored_version=$(yaml_read "$test_file" ".app.version" 2>/dev/null)
    assert_equals "1.0.0" "$restored_version" "版本也应该被恢复"
}

# 测试: 配置导出
test_yaml_export() {
    local test_file="$TEST_DIR/export_test.yaml"
    local export_dir="$TEST_DIR/exports"
    
    cat > "$test_file" << 'EOF'
app:
  name: MyApp
  port: 8080
EOF
    
    local export_file=$(yaml_export "$test_file" "$export_dir" 2>/dev/null)
    
    assert_not_empty "$export_file" "应该返回导出文件路径"
    assert_file_exists "$export_file" "导出文件应该存在"
    
    # 验证导出内容
    local exported_name=$(yaml_read "$export_file" ".app.name" 2>/dev/null)
    assert_equals "MyApp" "$exported_name" "导出文件应该包含原始数据"
}

# 测试: 配置导入
test_yaml_import() {
    local source_file="$TEST_DIR/import_source.yaml"
    local target_file="$TEST_DIR/import_target.yaml"
    
    cat > "$source_file" << 'EOF'
app:
  name: ImportedApp
  version: 2.0.0
  env: production
EOF
    
    cat > "$target_file" << 'EOF'
app:
  name: OldApp
  version: 1.0.0
EOF
    
    yaml_import "$source_file" "$target_file" 2>/dev/null
    
    local imported_name=$(yaml_read "$target_file" ".app.name" 2>/dev/null)
    assert_equals "ImportedApp" "$imported_name" "目标文件应该被导入的数据替换"
    
    local imported_env=$(yaml_read "$target_file" ".app.env" 2>/dev/null)
    assert_equals "production" "$imported_env" "新字段应该被导入"
}

# 测试: 多环境实际场景
test_yaml_real_world_scenario() {
    local base_config="$TEST_DIR/base_config.yaml"
    local dev_config="$TEST_DIR/dev_config.yaml"
    local prod_config="$TEST_DIR/prod_config.yaml"
    local final_config="$TEST_DIR/final_config.yaml"
    
    # 基础配置
    cat > "$base_config" << 'EOF'
app:
  name: MyApplication
  log_level: INFO
  features:
    feature_a: true
    feature_b: false
database:
  pool_size: 10
  timeout: 30
EOF
    
    # 开发环境配置
    cat > "$dev_config" << 'EOF'
app:
  log_level: DEBUG
  features:
    feature_b: true
database:
  host: localhost
  port: 3306
EOF
    
    # 生产环境配置
    cat > "$prod_config" << 'EOF'
app:
  log_level: WARN
database:
  host: prod-db.example.com
  port: 3306
  pool_size: 50
EOF
    
    # 测试开发环境合并
    yaml_merge_env "$base_config" "$dev_config" "$final_config" 2>/dev/null
    
    local dev_log_level=$(yaml_read "$final_config" ".app.log_level" 2>/dev/null)
    assert_equals "DEBUG" "$dev_log_level" "开发环境应该是DEBUG"
    
    local dev_db_host=$(yaml_read "$final_config" ".database.host" 2>/dev/null)
    assert_equals "localhost" "$dev_db_host" "开发环境数据库应该是localhost"
    
    # 测试生产环境合并
    yaml_merge_env "$base_config" "$prod_config" "$final_config" 2>/dev/null
    
    local prod_log_level=$(yaml_read "$final_config" ".app.log_level" 2>/dev/null)
    assert_equals "WARN" "$prod_log_level" "生产环境应该是WARN"
    
    local prod_pool=$(yaml_read "$final_config" ".database.pool_size" 2>/dev/null)
    assert_equals "50" "$prod_pool" "生产环境连接池应该是50"
}

###############################################################################
# 运行所有测试
###############################################################################

test_suite_start "适配层 - YAML配置管理适配器 (yaml.sh)"

run_test "读取配置值" test_yaml_read
run_test "写入配置值" test_yaml_write
run_test "删除配置键" test_yaml_delete
run_test "多环境配置合并" test_yaml_merge_env
run_test "计算配置哈希值 - 整个文件" test_yaml_get_hash_full
run_test "计算配置哈希值 - 指定键" test_yaml_get_hash_keys
run_test "保存和检查配置变更" test_yaml_save_check_changed
run_test "比较两个配置文件的值" test_yaml_compare_values
run_test "创建配置快照" test_yaml_create_snapshot
run_test "配置回滚" test_yaml_rollback
run_test "配置导出" test_yaml_export
run_test "配置导入" test_yaml_import
run_test "多环境实际场景" test_yaml_real_world_scenario

test_suite_end

# 清理测试目录
cleanup_test_dir "$TEST_DIR"

# 显示测试结果
print_test_summary

# 返回测试结果
exit_with_test_result

